﻿define([
    'Wjs/ui/elements/managers/zIndexManager',
    'Wjs/ui/widgets/tools/Tool',
    'Wjs/ui/widgets/fields/FieldBase',
    'Wjs/ui/elements/mixins/Loadable',
    'Wjs/ui/elements/mixins/SelectorHolder',
    'Wjs/ui/elements/util/Selector',
    'Wjs/ui/widgets/foundation/ViewHashContainer',
    'Wjs/ui/widgets/foundation/Overlay',
    'Wjs/ui/widgets/List'
],
function (zIndexManager) {
    'use strict';

    var HashContainer = Wjs.getType('HashContainer'),
        getComboBox;

    return Wjs.defineOnce({

        type: 'ComboBox',

        internal: {

            base: Wjs.getType('FieldBase'),

            mixins: [
                HashContainer,
                Wjs.getType('Loadable'),
                Wjs.getType('SelectorHolder') // Pass the selector the the children items
            ],

            onBeforeTypeDefined: function () {

                getComboBox = function (element) {

                    if (!element.comboBox) {

                        element.comboBox = element.up(function (p) {

                            return p instanceof Wjs.getType('ComboBox');
                        })
                    }

                    return element.comboBox;
                };

                // A delay to hide the box
                Wjs.define({

                    type: 'ComboHideBoxDelay',

                    internal: {

                        construct: function () {

                            this.delay = 500;
                        }
                    },

                    setBox: function (box) {

                        this.box = box;

                        return this;
                    },

                    setDelay: function (time) {

                        this.delay = time; // milliseconds

                        return this;
                    },

                    // Schedule the box to be hidden
                    start: function () {

                        if (!this.box.isHidden()) {

                            this.timer = Wjs.delay(function () {

                                this.box.hide();

                                this.timer = null;
                            },
                            this.delay,
                            this);
                        }

                    },

                    // Cancels hiding the box
                    cancel: function () {

                        if (this.timer) {

                            this.timer.cancel();

                            this.timer = null;
                        }
                    }

                });

                Wjs.define({

                    type: 'ComboBoxDisplay',

                    internal: {

                        base: Wjs.getType('ViewHashContainer'),

                        initialize: function () {

                            this.addClass('wjs-comboBox-display  wjs-clickable');

                            this.addSection('content', // Displays the selected text

                                Wjs.new('ViewSingleContainer')
                                    .addClass('wjs-comboBox-display-content')
                            );

                            this.addSection('arrow', // Toggles the box on and off

                                Wjs.new('Tool')
                                    .addClass('wjs-comboBox-arrow')
                            );

                            this.addEvents({

                                click: function () {

                                    this.getParent().toggleOptions();
                                },

                                mouseenter: function (evt) {

                                    getComboBox(this).getHideBoxDelay().cancel(); // Cancel the timer if the mouse entered the box, do nothing otherwise
                                },

                                mouseleave: function (evt) {

                                    getComboBox(this).getHideBoxDelay().start(); // Schedule the box to be hidden
                                }

                            });
                        }
                    },

                    getArrow: function () {

                        return this.getSection('arrow');
                    },

                    getContent: function () {

                        return this.getSection('content');
                    }

                });

                Wjs.define({

                    type: 'ComboBoxBox', // Pops up the box to select the item(s)

                    internal: {

                        base: Wjs.getType('Overlay'),

                        initialize: function () {
                            var me = this;

                            me.addClass('wjs-comboBox-box');

                            me.addEvents({

                                mouseenter: function (evt) {

                                    getComboBox(me).getHideBoxDelay().cancel(); // Cancel the timer if the mouse entered the box, do nothing otherwise
                                },

                                mouseleave: function (evt) {

                                    getComboBox(me).getHideBoxDelay().start(); // Schedule the box to be hidden
                                }

                            });

                            me.hide(); // Initially hidden

                            // Create the default child container that will have the items to select
                            me.setItem(

                                Wjs.new('List')

                                    .setWidth('100%')

                                    //.setSelector(selector) Set in the combo box
                            );

                            me.onBeforeShow(function () { // setUseVisibility is set so the element is measurable when hidden
                                var display = getComboBox(this).getDisplay(),
                                    viewportHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
                                    maxHeight = viewportHeight - display.getBottom();

                                me.setStyle({
                                    'max-height': maxHeight + 'px'
                                })

                            });

                            
                        }

                    },

                    showHandler: function () {

                        zIndexManager.add(this);
                    },

                    hideHandler: function () {

                        zIndexManager.remove(this);
                    },

                    itemAddedHandler: function (child) {
                        var me = this;

                        child.selectionChangedHandler = function (cfg) {

                            me.selectionChangedHandler(cfg);
                        };
                    },

                    selectionChangedHandler: function (cfg) {
                        var combo = this.up(function (p) {
                            return p.type == 'ComboBox';
                        }),
                        child = this.getItem(),
                        selection = child.getSelection(),
                        isMultipleSelection = child.isMultipleSelection();

                        switch (cfg.selectionType) {
                            case 'select':
                                {
                                    combo.setDisplayContent(selection);

                                    if (!isMultipleSelection) {

                                        this.hide();
                                    }
                                }
                                break;

                            case 'deselect':
                                {
                                    if (isMultipleSelection) {

                                        combo.setDisplayContent(selection);
                                    }
                                }
                                break;

                            default:
                                {
                                    throw 'selectionChangedHandler method is not implemented for selection type: ' + cfg.selectionType;
                                }
                                break;
                        }

                        if (combo.selectionChangedHandler) {

                            combo.selectionChangedHandler(cfg);
                        }

                        if (cfg.fireFieldChanged) {

                            combo.fireFieldChanged();
                        }
                    },

                    //#region Called by the grid and delegated to the child

                    loadData: function (data, contentType) {

                        this.getItem().loadData(data, contentType);

                        return this;
                    },

                    // Selects the first record of the box if there is none selected
                    selectFirstRecord: function () {
                        var firstChild,
                            item = this.getItem(); // The single item of the box

                        // If there are options and there is no selected, selected the first one
                        if (item.getItems().length > 0 && item.getSelection().length == 0) {

                            firstChild = item.getItem(0);

                            firstChild.select({
                                acceptChanges: true // Do not set the field as modified
                            });
                        }
                    },

                    //getItems: function () {

                    //    return this.getItem().getItems(); // This getItems is a view specific method, for example: grid returns rows, list returns list items, etc
                    //},

                    //getItem: function (i) {

                    //    return this.getItems()[i];
                    //},

                    getSelection: function () {

                        return this.getItem().getSelection();
                    },

                    //#endregion

                    //#region Selector delegation methods

                    acceptChanges: function () {

                        this.getItem().acceptChanges();
                    },

                    isModified: function () {

                        return this.getItem().isModified();
                    }

                    //#endregion
                });
            },

            initialize: function () {
                var box;

                this.addClass('wjs-comboBox');

                // Create the components before setting the text field
                this.setDisplay(

                    Wjs.new('ComboBoxDisplay')
                );

                box = Wjs.new('ComboBoxBox')

                this.addSection('box', box);

                this.hideBoxDelay = Wjs.new('ComboHideBoxDelay')
                                        .setBox(box);

                this.setTextField('Text');

                this.setValueField('Value');

                this.setSelector( // Set a default single selection selector

                    Wjs.new('Selector')
                );
            },
        },

        getHideBoxDelay: function () {

            return this.hideBoxDelay;
        },

        setTextField: function (textField) {

            this.textField = textField;

            this.getBox().getItem().setDraw(function (data) {

                return data[textField];
            });

            return this;
        },

        setValueField: function (valueField) {

            this.valueField = valueField;

            return this;
        },

        setSelector: function (selector) {

            this.getBox().getItem().setSelector(selector); // Pass the selector to the container of the box

            return this;
        },

        setMultipleSelection: function (multipleSelection) {

            this.getBox().getSelector().setMultipleSelection(multipleSelection);

            return this;
        },

        getMultipleSelection: function() {

            return this.getBox().getSelector().getMultipleSelection();
        },

        setNotSelectedText: function (notSelectedText) {

            this.notSelectedText = notSelectedText;

            return this;
        },

        setNotSelectedValue: function (notSelectedValue) {

            this.notSelectedValue = notSelectedValue;

            return this;
        },

        setDisplay: function (display) {

            if (this.getDisplay()) {

                this.replaceSection('display', display);
            }
            else {

                this.addSection('display', display);
            }

            return this;
        },

        getDisplay: function () {

            return this.getSection('display');
        },

        //setBox: function (box) {

        //    //box.setMultipleSelection(this.getMultipleSelection()); // Synchronize the selection mode

        //    if (this.getSection('box')) {

        //        this.replaceSection('box', box);
        //    }
        //    else {

        //        this.addSection('box', box);
        //    }

        //    return this;
        //},

        setBoxItem: function (item) {

            this.getBox().setItem(item);

            return this;
        },

        getBox: function () {

            return this.getSection('box');
        },

        // Adds the not selected text and value to the first data record
        setNotSelectedRecord: function (data) {
            var obj = null;

            // If the empty text or value fields are set
            if (Wjs.isDefined(this.notSelectedText) || Wjs.isDefined(this.notSelectedValue)) {

                obj = {};

                obj[this.textField] = this.notSelectedText;
                obj[this.valueField] = this.notSelectedValue;

                data.unshift(obj); // Prepend even if empty
            }
        },

        loadHandler: function (data, contentType) {
            var box = this.getBox();

            this.setNotSelectedRecord(data);

            box.loadData(data, contentType); // The options grid is fully constructed. Load the data now

            box.selectFirstRecord();

            this.acceptChanges();
        },

        getSelection: function () {

            return this.getBox().getSelection(); // Returns the selected items of the box
        },

        getSelectedContents: function () {
            var output = [];

            this.getBox().getSelection().forEach(function (item) {

                output.push(item.getRecord()[this.textField]);
            },
            this);

            return output;
        },

        getSelectedValues: function () {
            var container = this.getBox().getItem(),
                output = [];

            this.getBox().getSelection().forEach(function (item) {

                output.push(item.record[this.valueField]);
            },
            this);

            return output;
        },

        setDisplayContent: function (selection) {
            var content;

            if (this.renderDisplay) { // Custom rendering

                content = this.renderDisplay(selection);
            }
            else { // Do the default display

                switch (selection.length) {

                    case 0: // No selection
                        {
                            content = "Select";
                        }
                        break;

                    case 1: // Single selection
                        {
                            content = this.getSelectedContents()[0];
                        }
                        break;

                    default: // Multiple selection
                        {
                            content = "Multiple";
                        }
                        break;
                }

            }

            this.getDisplay().getContent().setHtml(content);
        },

        addOption: function (cfg) {

            this.getBox().addRow(cfg);
        },

        clear: function () {

            this.getBox().clearRows();
        },

        toggleOptions: function () {
            var box = this.getBox();

            if (box.hidden) {

                box.show();
            }
            else {

                box.hide();
            }
        },

        // Selects the option that has given value
        setValue: function (val, opts) {
            var container = this.getBox().getItem(),
                row;

            container.getItems().forEach(function (item) {

                if (item.getRecord()[this.valueField] == val) {

                    row = item;
                }
            },
            this);

            if (row) {

                row.select(opts);
            }
            else {

                throw ('Option with value: ' + val + ' does not exist');
            }

            return this;
        },

        getValue: function () {
            var me = this,
                selection = me.getBox().getSelection(),
                values,
                i,
                record;

            if (!selection) {

                return null;
            }

            if (me.extractValues) { // Custom value extraction

                return me.extractValues(selection);
            }
            else {

                if (me.multipleSelect) {

                    values = [];

                    for (i = 0; i < selection.length; ++i) {

                        record = selection[i].getRecord();

                        values.push(record[me.valueField]);
                    }

                    return values;
                }
                else { // Single select

                    record = selection[0].getRecord();

                    return record[me.valueField];
                }
            }
        },

        acceptChanges: function () {

            this.getBox().acceptChanges();
        },

        isModified: function () {

            return this.getBox().isModified();
        }

    });

});